const mongoConnected = require("../lib/mongoose_connect");
const mongoModels = require("../lib/mongoose_schemas");

mongoConnected.then(async _test => {
  // await calcKills("match.bro.official.pc-2018-06.steam.duo.na.2020.02.14.04.c7d87a9d-4e9e-45fb-9bf7-428acf42336f");
  // await calcKills("match.bro.official.pc-2018-06.steam.squad.na.2020.02.02.13.1baaee3b-b5f8-4833-955b-0fa8a81abc52");
  // await calcKills("match.bro.official.pc-2018-06.steam.squad.na.2020.02.15.13.e6130792-e9e7-4a04-a6af-1a22f57934df");
  // let findFrequentPlayers = await mongoModels.Player.aggregate([{
  //   $match: {
  //     "match_ids.1": { $exists: true }
  //   }
  // }, {
  //   $project: {
  //     match_ids: 1,
  //     names: 1,
  //     length: { $size: "$match_ids" }
  //   }
  // }, {
  //   $sort: {
  //     length: -1
  //   }
  // }]);
  // let NO1Player = findFrequentPlayers[0];
  // for (let match_id of NO1Player.match_ids) {
  //   let test = await calcIndicators(match_id);
  //   debugger
  //   await mongoModels.PlayerPerformanceIndicator.insertMany(test.map(e => new mongoModels.PlayerPerformanceIndicator(e)))
  //   debugger
  // }

  let o_matches = await mongoModels.Match.find({});
  for (let match of o_matches) {
    console.log(Date());
    let o_calcIndicators = await calcIndicators(match.match_id);
    await mongoModels.PlayerPerformanceIndicator
      .insertMany(o_calcIndicators.map(e =>
        new mongoModels.PlayerPerformanceIndicator(e)))
    console.log(Date(), '++========++')
  }
  debugger
});

/**
 * @description 得到每个人的kill的时间(时间用来计算kill streak),然后能得到kill rank,得到每个assist的assist数量,
 * 根据teamid能够检测到杀的人是不是队友或者自己
 * @returns {Promise<{
 * killEnemies:Object.<string,Date[]>,
 * assist:Object.<string,number>,
 * killEnemyWithHeadShot:Object.<string,number>,
 * killEnemyBySniper:Object.<string,number>,
 * killEnemyMaxDistance:Object.<string,number>,
 * killerInVehicle:Object.<string,number>,
 * killHimself:Object.<string,number>,
 * killTeammate:Object.<string,number>,
 * killByVehicleExplosion:{Object.<string,number>}
 * }>}
 * @param {String} match_id 
 */
function calcKills(match_id) {
  return new Promise(async resolve => {
    let killEvents = await mongoModels.Event.find({
      match_id: match_id,
      _T: "LogPlayerKill"
    });
    /**
     * @type {Object.<string,Date[]>}
     */
    let killEnemies = {};//id:[killTime]
    /**@type {Object.<string,number>} */
    let assist = {};//id:Number
    /**@type {Object.<string,number>} */
    let killEnemyWithHeadShot = {};//id:Number
    /**@type {Object.<string,number>} */
    let killEnemyBySniper = {};//id:Number
    /**@type {Object.<string,number>} */
    let killEnemyMaxDistance = {};//id:MaxKillDistance (number)
    /**@type {Object.<string,number>} */
    let killerInVehicle = {};
    /**@type {Object.<string,number>} */
    let killHimself = {};
    /**@type {Object.<string,number>} */
    let killTeammate = {};
    /**@type {Object.<string,number>} */
    let killByVehicleExplosion = {};

    // debugger
    for (let e of killEvents) {
      let killer = e._doc['killer'];
      let victim = e._doc['victim'];
      let isKillHimself = false;
      if (e._doc.assistant && e._doc.assistant.accountId
        && e._doc.assistant.accountId != killer.accountId) {
        //要避免assistant和killer一样的情况 我发现数据里确实存在
        let ai = e._doc.assistant.accountId;
        if (!assist[ai]) {
          assist[ai] = 1;
        } else {
          assist[ai] += 1;
        }
      }

      if (!killer) {
        // debugger
        // console.log("no killer,reason is", e._doc.damageTypeCategory);
        continue;
      }
      if (killer['teamId'] == victim['teamId']) {
        if (killer.name != victim.name) {
          // debugger
          killTeammate[killer.accountId]
            ? killTeammate[killer.accountId] + 1
            : killTeammate[killer.accountId] = 1;
        } else {
          // debugger
          isKillHimself = true;
          killHimself[killer.accountId]
            ? killHimself[killer.accountId] + 1
            : killHimself[killer.accountId] = 1;
        }
      } else {
        killEnemies[killer.accountId]
          ? killEnemies[killer.accountId].push(e._D)
          : killEnemies[killer.accountId] = [e._D];

        if (e._doc.damageReason == "HeadShot") {
          killEnemyWithHeadShot[killer.accountId]
            ? killEnemyWithHeadShot[killer.accountId] + 1
            : killEnemyWithHeadShot[killer.accountId] = 1;
        }

        let sniperDamageCauserNames = ["WeapKar98k_C", "WeapAWM_C", "WeapM24_C", "WeapWin94_C"]
        if (sniperDamageCauserNames.includes(e._doc.damageCauserName)) {
          killEnemyBySniper[killer.accountId]
            ? killEnemyBySniper[killer.accountId] + 1
            : killEnemyBySniper[killer.accountId] = 1;
        }
        let the_distance = e._doc.distance;
        // if (the_distance >= 10000) {
        //   debugger
        //   let attackEvent = await mongoModels.Event.findOne({
        //     _T: "LogPlayerAttack",
        //     attackId: e._doc.attackId,
        //     match_id: match_id
        //   });
        //   let groggy = await mongoModels.Event.findOne({
        //     dBNOId: e._doc.dBNOId,
        //     _T: "LogPlayerMakeGroggy",
        //     match_id: match_id
        //   });
        //   debugger

        // }
        let knowAttackerInVehicleOrNot = false;

        if (e._doc.dBNOId >= 1) {
          let groggy = await mongoModels.Event.findOne({
            dBNOId: e._doc.dBNOId,
            _T: "LogPlayerMakeGroggy",
            match_id: match_id
          });
          knowAttackerInVehicleOrNot = true;
          the_distance = groggy._doc.distance;
          if (groggy._doc.isAttackerInVehicle) {
            killerInVehicle[killer.accountId]
              ? killerInVehicle[killer.accountId] + 1
              : killerInVehicle[killer.accountId] = 1;
          }

        }


        killEnemyMaxDistance[killer.accountId]
          ? (killEnemyMaxDistance[killer.accountId] = (killEnemyMaxDistance[killer.accountId] > the_distance) ? killEnemyMaxDistance[killer.accountId] : the_distance)
          : killEnemyMaxDistance[killer.accountId] = the_distance;

        if (e._doc.damageTypeCategory == "Damage_VehicleHit") {
          knowAttackerInVehicleOrNot = true;
          killerInVehicle[killer.accountId]
            ? killerInVehicle[killer.accountId] + 1
            : killerInVehicle[killer.accountId] = 1;
        }
        if (e._doc.damageTypeCategory == "Damage_Explosion_Vehicle") {
          killByVehicleExplosion[killer.accountId]
            ? killByVehicleExplosion[killer.accountId] + 1
            : killByVehicleExplosion[killer.accountId] = 1;
        }
        if (!knowAttackerInVehicleOrNot) {
          if ((!(e._doc.attackId > 0)) && !["Damage_Explosion_Vehicle"].includes(e._doc.damageTypeCategory)) {
            debugger
          }
          if (e._doc.attackId > 0) {
            let attackEvent = await mongoModels.Event.findOne({
              _T: "LogPlayerAttack",
              attackId: e._doc.attackId,
              match_id: match_id
            });
            if (!attackEvent) {
              debugger
              e
            }
            if (attackEvent && attackEvent._doc.vehicle) {
              killerInVehicle[killer.accountId]
                ? killerInVehicle[killer.accountId] + 1
                : killerInVehicle[killer.accountId] = 1;
            }
          }

          // debugger
        }
      }




      // debugger
    }
    // debugger
    resolve({
      killEnemies: killEnemies,
      assist: assist,
      killEnemyWithHeadShot: killEnemyWithHeadShot,
      killEnemyBySniper: killEnemyBySniper,
      killEnemyMaxDistance: killEnemyMaxDistance,
      killerInVehicle: killerInVehicle,
      killHimself: killHimself,
      killTeammate: killTeammate,
      killByVehicleExplosion: killByVehicleExplosion
    })


  })
}

/**
 * @returns {Promise<{
 * damageToEnemy:Object.<string,number>, 
 * damageTaken:Object.<string,number>, 
 * healAmount:Object.<string,number>
 * }>}
 * @description 计算每个玩家给敌人的伤害,自己收到的伤害,以及自己回血的生命量
 * @param {String} match_id 
 */
function calcDamageAndHeal(match_id) {
  return new Promise(async resolve => {
    let damageEvents = await mongoModels.Event.find({
      _T: "LogPlayerTakeDamage",
      match_id: match_id
    });
    let healEvents = await mongoModels.Event.find({
      _T: "LogHeal",
      match_id: match_id
    });
    /**@type {Object.<string,number>[]} */
    let [damageToEnemy, damageTaken, healAmount] = [{}, {}, {}];
    for (let e of healEvents) {
      // debugger
      healAmount[e._doc.character.accountId]
        ? healAmount[e._doc.character.accountId] += e._doc.healAmount
        : healAmount[e._doc.character.accountId] = e._doc.healAmount
    }
    for (let e of damageEvents) {
      // debugger
      if (e._doc.attacker
        && e._doc.attacker.accountId
        && e._doc.victim
        && e._doc.victim.accountId
        && e._doc.damage
        && e._doc.victim.teamId != e._doc.attacker.teamId) {
        damageToEnemy[e._doc.attacker.accountId]
          ? damageToEnemy[e._doc.attacker.accountId] += e._doc.damage
          : damageToEnemy[e._doc.attacker.accountId] = e._doc.damage;
        damageTaken[e._doc.victim.accountId]
          ? damageTaken[e._doc.victim.accountId] += e._doc.damage
          : damageTaken[e._doc.victim.accountId] = e._doc.damage;
      }
    }

    resolve({
      damageTaken: damageTaken,
      damageToEnemy: damageToEnemy,
      healAmount: healAmount
    })

  })
}


/**
 * @returns {Promise<{
  knockEnemies:Object.<string,number>, 
  * }>}
  * @description 计算每个玩家给敌人的伤害,自己收到的伤害,以及自己回血的生命量
  * @param {String} match_id 
  */
function calcKnockDownGroggy(match_id) {
  return new Promise(async resolve => {
    /**@type {Object.<string,number>} */
    let knockEnemy = {}
    let groggyEvents = await mongoModels.Event.find({
      match_id: match_id,
      _T: "LogPlayerMakeGroggy"
    });
    for (let e of groggyEvents) {
      if (e._doc.attacker
        && e._doc.attacker.accountId
        && e._doc.victim
        && e._doc.victim.accountId
        && e._doc.victim.teamId != e._doc.attacker.teamId) {
        knockEnemy[e._doc.attacker.accountId] ?
          knockEnemy[e._doc.attacker.accountId] += 1 :
          knockEnemy[e._doc.attacker.accountId] = 1;
      }
    }
    resolve({
      knockEnemies: knockEnemy
    })
  })
}


/**
 * @returns {Promise<{
  * distanceOnFoot:Object.<string,number>, 
  * distanceOnSwim:Object.<string,number>, 
  * distanceOnVehicle:Object.<string,number>, 
  * survivTime:Object.<string,number>,
  * match_time_use:Number 
  * }>}
 * @param {String} match_id 
 */
function calcMoveDistanceAndSurvivTime(match_id) {
  return new Promise(async resolve => {
    let killEvents = await mongoModels.Event.find({
      match_id: match_id,
      _T: "LogPlayerKill"
    });
    let matchEndEvent = await mongoModels.Event.findOne({
      match_id: match_id,
      _T: "LogMatchEnd"
    });
    let matchStartEvent = await mongoModels.Event.findOne({
      match_id: match_id,
      _T: "LogMatchStart"
    });
    /**@type {Object.<string,number>[]} */
    let [distanceOnFoot, distanceOnSwim, distanceOnVehicle,
      survivTime] = [{}, {}, {}, {}];
    let startTime = matchStartEvent._D.valueOf();
    let endTime = matchEndEvent._D.valueOf();
    let match_time_use = endTime - startTime;
    // debugger
    for (let e of killEvents) {
      // debugger
      if (e._doc.victimGameResult
        && e._doc.victimGameResult.stats) {
        let accountId = e._doc.victimGameResult.accountId
        distanceOnFoot[accountId] = e._doc.victimGameResult.stats.distanceOnFoot;
        distanceOnSwim[accountId] = e._doc.victimGameResult.stats.distanceOnSwim;
        distanceOnVehicle[accountId] = e._doc.victimGameResult.stats.distanceOnSwim
      }
      if (e._doc.victim && e._doc.victim.accountId) {
        let deathTime = e._D.valueOf();
        survivTime[e._doc.victim.accountId] = deathTime - startTime;
      }
    }
    for (let r of matchEndEvent._doc.gameResultOnFinished.results) {
      let accountId = r.accountId;
      distanceOnFoot[accountId] = r.stats.distanceOnFoot;
      distanceOnSwim[accountId] = r.stats.distanceOnSwim;
      distanceOnVehicle[accountId] = r.stats.distanceOnVehicle;
      if (!survivTime[accountId]) {
        survivTime[accountId] = match_time_use;
      }
    }
    resolve({
      distanceOnFoot,
      distanceOnSwim,
      distanceOnVehicle,
      survivTime,
      match_time_use
    })

  })
}

/**
 * @return {Promise<{
 * vehicleDestroy:Object.<string,number>
 * }>}
 * @param {String} match_id 
 */
function calcVehicleDestroy(match_id) {
  return new Promise(async resolve => {
    // console.log("match", match_id)
    /**@type {Object.<string,number>}  */
    let vehicleDestroy = {}
    let destroyEvents = await mongoModels.Event.find({
      match_id: match_id,
      _T: "LogVehicleDestroy"
    });
    for (let de of destroyEvents) {
      if (de._doc.attackId > 1
        && de._doc.attacker
        && de._doc.attacker.accountId) {
        // debugger
        // console.log(de._doc.attackId)
        let damageEvents = await mongoModels.Event.find({
          match_id: match_id,
          // _T:"LogPlayerTakeDamage",
          // _T: { $nin: ["LogVehicleDestroy", "LogPlayerAttack", "LogPlayerUseThrowable", "LogWheelDestroy"] },
          _T: { $in: ["LogPlayerTakeDamage", "LogPlayerKill", "LogPlayerMakeGroggy", "LogArmorDestroy"] },
          // _T: { $ne: "LogPlayerAttack" },
          attackId: de._doc.attackId
        });
        if (damageEvents.length) {
          // debugger
          vehicleDestroy[de._doc.attacker.accountId] ?
            vehicleDestroy[de._doc.attacker.accountId] += 1 :
            vehicleDestroy[de._doc.attacker.accountId] = 1
        }
      }
    }
    // debugger
    // console.log("-------------")
    resolve({ vehicleDestroy })
  })
}

/**
 * @returns {Promise<{weaponEquip:Object.<string,number>,
 * throwableUse:Object.<string,number>,}>}
 * @param {String} match_id 
 */
function calcWeapon(match_id) {
  return new Promise(async resolve => {
    let equipEvents = await mongoModels.Event.find({
      _T: "LogItemEquip",
      match_id: match_id,
      "item.category": "Weapon"
    });
    let throwableEvents = await mongoModels.Event.find({
      _T: "LogPlayerUseThrowable",
      match_id: match_id
    });
    /**@type {Object.<string,number>[]} */
    let [weaponEquip, throwableUse] = [{}, {}];
    // debugger
    for (let e of equipEvents) {
      if (e._doc.character
        && e._doc.character.accountId
        && e._doc.item.category == "Weapon") {
        weaponEquip[e._doc.character.accountId] ?
          weaponEquip[e._doc.character.accountId] += 1 :
          weaponEquip[e._doc.character.accountId] = 1
      }
    }
    for (let e of throwableEvents) {
      if (e._doc.attacker
        && e._doc.attacker.accountId
        && e._doc.weapon.subCategory == "Throwable") {
        throwableUse[e._doc.attacker.accountId] ?
          throwableUse[e._doc.attacker.accountId] += 1 :
          throwableUse[e._doc.attacker.accountId] = 1
      }
    }
    resolve({
      throwableUse,
      weaponEquip
    })
  })
}

/**
 * @returns {Promise<{boostUse:Object.<string,number>>}
 * @param {String} match_id 
 */
function calcBoostUse(match_id) {
  return new Promise(async resolve => {
    let events = await mongoModels.Event.find({
      _T: "LogItemUse",
      match_id: match_id,
      "item.subCategory": "Boost"
    });
    /**@type {Object.<string,number>} */
    let boostUse = {};
    for (let e of events) {
      // debugger
      boostUse[e._doc.character.accountId] ?
        boostUse[e._doc.character.accountId] += 1 :
        boostUse[e._doc.character.accountId] = 1
    }

    resolve({
      boostUse
    })
  })
}

/**
   * @returns {Promise<Array<{
    * account_id:String,
    * match_id:String,
    * kill_enemy:Number,
    kill_per_km:Number,
    knock_enemy:Number,
    knock_per_km:Number,
    assist:Number,
    kill_headshot:Number,
    kill_by_sniper:Number,
    kill_by_vehicle_explosion:Number,
    max_kill_distance:Number,
    killer_in_vehicle:Number,
    kill_himself:Number,
    kill_teammate:Number,
    damage_to_enemy:Number,
    damage_take:Number,
    heal_amount:Number,
    distance_on_foot:Number,
    distance_on_swim:Number,
    distance_on_vehicle:Number,
    distance_total:Number,
    survive_time:Number,
    good_vehicle_destroy:Number,
    weapon_equip:Number,
    use_throwable:Number,
    use_boost:Number,
    * }>>}
 * @param {String} match_id 
 */
function calcIndicators(match_id) {
  /**
   * @returns {Object.<string,number>}
   * @param {String[]} player_account_ids 
   */
  let genByPlayerList = (player_account_ids) => {
    let obj = {};
    for (let ai of player_account_ids) {
      obj[ai] = 0
    }
    return obj;
  }


  /**


   * @param {Object.<string,number>} result_obj 包含完整的玩家列表,初始值为0
   * @param {Object.<string,number>} info_obj 可能玩家列表不完整,从calc函数计算来的
   */
  let merge = (result_obj, info_obj) => {
    for (let h in info_obj) {
      if (info_obj[h] > result_obj[h]) {
        result_obj[h] = info_obj[h]
      }
    }
    return result_obj;
  }
  return new Promise(async resolve => {
    let match_info = await mongoModels.Match.findOne({
      match_id: match_id
    });
    let player_accountIds = match_info.players.map(e => e.account_id);
    // debugger
    let o_kill = await calcKills(match_id);
    let o_distanceAndSurvivTime = await calcMoveDistanceAndSurvivTime(match_id);
    let o_KnockDownGroggy = await calcKnockDownGroggy(match_id);
    let killEnemies = (() => {
      let res = genByPlayerList(player_accountIds);
      let info_raw = o_kill.killEnemies;
      let info = {};
      for (let h in info_raw) {
        info[h] = info_raw[h].length
      }
      return merge(res, info);
    })();
    let knockEnemies = merge(genByPlayerList(player_accountIds), o_KnockDownGroggy.knockEnemies);
    let travelDistance = (() => {
      let totalDistance = {};
      for (let h in o_distanceAndSurvivTime.distanceOnFoot) {
        totalDistance[h] = o_distanceAndSurvivTime.distanceOnFoot[h];
      }
      for (let h in o_distanceAndSurvivTime.distanceOnSwim) {
        totalDistance[h] += o_distanceAndSurvivTime.distanceOnSwim[h];
      }
      for (let h in o_distanceAndSurvivTime.distanceOnVehicle) {
        totalDistance[h] += o_distanceAndSurvivTime.distanceOnVehicle[h];
      }
      return merge(genByPlayerList(player_accountIds), totalDistance)
    })();
    let [kill_per_km, knock_per_km] = (() => {
      let res_kill = genByPlayerList(player_accountIds);
      let res_knock = genByPlayerList(player_accountIds);
      for (let h in res_kill) {
        let kills = killEnemies[h];
        let distance = travelDistance[h];

        res_kill[h] = kills / (distance / 1000)
        if (kills == 0) {
          res_kill[h] = 0;

        }
        let knocks = knockEnemies[h];

        res_knock[h] = knocks / (distance / 1000);
        if (knocks == 0) {
          res_knock[h] = 0;
        }

      }
      return [res_kill, res_knock]
    })();

    let surviveTime = merge(genByPlayerList(player_accountIds),
      o_distanceAndSurvivTime.survivTime);
    let swimDis = merge(genByPlayerList(player_accountIds),
      o_distanceAndSurvivTime.distanceOnSwim);
    let footDis = merge(genByPlayerList(player_accountIds),
      o_distanceAndSurvivTime.distanceOnFoot);
    let vehicleDis = merge(genByPlayerList(player_accountIds),
      o_distanceAndSurvivTime.distanceOnVehicle);
    let assist = merge(genByPlayerList(player_accountIds),
      o_kill.assist);
    let kill_headshot = merge(genByPlayerList(player_accountIds),
      o_kill.killEnemyWithHeadShot);
    let kill_bySniper = merge(genByPlayerList(player_accountIds),
      o_kill.killEnemyBySniper);
    let max_kill_dis = merge(genByPlayerList(player_accountIds),
      o_kill.killEnemyMaxDistance);
    let kill_byVehicleExplosion = merge(genByPlayerList(player_accountIds),
      o_kill.killByVehicleExplosion);
    let killer_in_vehicle = merge(genByPlayerList(player_accountIds),
      o_kill.killerInVehicle);
    let kill_himself = merge(genByPlayerList(player_accountIds),
      o_kill.killHimself);
    let kill_teammate = merge(genByPlayerList(player_accountIds),
      o_kill.killTeammate);

    let o_VehicleDestroy = await calcVehicleDestroy(match_id);
    let good_vehicle_destroy = merge(genByPlayerList(player_accountIds),
      o_VehicleDestroy.vehicleDestroy)
    let o_weapon = await calcWeapon(match_id);
    let weapon_equip = merge(genByPlayerList(player_accountIds),
      o_weapon.weaponEquip);
    let throwable_use = merge(genByPlayerList(player_accountIds),
      o_weapon.throwableUse);
    let o_boostuse = await calcBoostUse(match_id);
    let boost_use = merge(genByPlayerList(player_accountIds),
      o_boostuse.boostUse);

    let o_damageAndHeal = await calcDamageAndHeal(match_id);
    let damage_to_enemy = merge(genByPlayerList(player_accountIds),
      o_damageAndHeal.damageToEnemy);
    let damage_take = merge(genByPlayerList(player_accountIds),
      o_damageAndHeal.damageTaken);
    let heal_amount = merge(genByPlayerList(player_accountIds),
      o_damageAndHeal.healAmount);

    // debugger
    console.log(match_id, "done")
    // debugger
    let res = [];
    for (let account_id of player_accountIds) {
      let obj = {
        account_id: account_id,
        match_id: match_id,
        kill_enemy: killEnemies[account_id],
        kill_per_km: kill_per_km[account_id],
        knock_enemy: knockEnemies[account_id],
        knock_per_km: knock_per_km[account_id],
        assist: assist[account_id],
        kill_headshot: kill_headshot[account_id],
        kill_by_sniper: kill_bySniper[account_id],
        kill_by_vehicle_explosion: kill_byVehicleExplosion[account_id],
        max_kill_distance: max_kill_dis[account_id],
        killer_in_vehicle: killer_in_vehicle[account_id],
        kill_himself: kill_himself[account_id],
        kill_teammate: kill_teammate[account_id],
        damage_to_enemy: damage_to_enemy[account_id],
        damage_take: damage_take[account_id],
        heal_amount: heal_amount[account_id],
        distance_on_foot: footDis[account_id],
        distance_on_swim: swimDis[account_id],
        distance_on_vehicle: vehicleDis[account_id],
        distance_total: travelDistance[account_id],
        survive_time: surviveTime[account_id],
        good_vehicle_destroy: good_vehicle_destroy[account_id],
        weapon_equip: weapon_equip[account_id],
        use_throwable: throwable_use[account_id],
        use_boost: boost_use[account_id]
      }
      res.push(obj);
    }
    resolve(res);
  })
}






